<template>
  <el-cascader
    v-bind="$attrs"
    popper-class="v-cascader-hide-radio"
    v-loading="loading"
    :props="props"
    :options="options"
    :clearable="clearable"
    :class="css"
    :value="value"
    @change="handleCascaderChange"
  >
    <div slot-scope="{ node, data }" @click="handleItemChange(node, data)">
      <span>{{ data[props.label] }}</span>
    </div>
  </el-cascader>
</template>
<script>
/**
 * 对比之前的el-cascader
 * > 没有change-on-select，也就没有点击选中，只能通过设置 props checkStrictly=true 来启用选中任一级功能
 * > 启用选中任一级后，只能选择 radio 才能触发选中事件，这会改变之前用户操作的习惯
 * > 通过 default slot 自定义click事件，手动emit value，达到点击item即选中的目的，同时用css隐藏radio
 * > 新版本的el-cascader不用手动获取每一级的label了（default slot提供了node回调，node.pathLabels即可获取），但在初始化时要自己处理
 * > TODO: 点击最后一级的时候，关闭浮层（之前的用户操作，el-cascader没有提供关闭浮层的api）
 */
const noop = () => {
  return [true, []]
}
export default {
  components: {},
  model: {
    prop: 'value',
    event: 'change',
  },
  props: {
    value: String,
    fetchData: { type: Function, required: true },
    structure: { type: Array, required: true }, // 级别结构
    bottomLevel: Number, // 限制的最后一级（在级别结构中的位置）
    topValue: { type: String, default: '' }, // 第一级的值
    topAsFirst: Boolean, // 第一级的值为顶级ID
    autoInit: { type: Boolean, default: true }, // 自动根据传入值取第一级
    hasChildren: { type: Function, default: noop }, // 用于判断下一级是否存在的方法
  },
  data() {
    return {
      options: [],
      select: '',
      selectedLabel: [],
      hasInit: false,
      loading: false,
    }
  },
  computed: {
    // 级别累加长度
    levelLength() {
      return this.structure.reduce((v, o) => v + o)
    },
    lastDeepth() {
      return this.topAsFirst ? 1 : 0
    },
    lastLevel() {
      return this.bottomLevel || this.structure.length - 2
    },
    clearable() {
      return this.$attrs && this.$attrs.clearable !== false
    },
    css() {
      return (this.$attrs && this.$attrs.class) || 'w-100'
    },
    props() {
      const props = (this.$attrs && this.$attrs.props) || {}
      props.lazy = true
      props.lazyLoad = this.lazyLoad
      props.checkStrictly = true
      return props
    },
  },
  watch: {
    value(val) {
      if (val !== this.select) {
        this.init(val)
      }
    },
    topValue() {
      this.init()
    },
  },
  created() {},
  mounted() {},
  methods: {
    init(val = this.value, top = this.topValue) {
      this.select = val
      this.loading = true
      this.getValue(top).then(res => {
        this.options = this.getChildrenOptions(top, res)
        this.selectedLabel = []
        if (val) {
          const selects = this.getSelects(val)
          this.setInitOption([...selects], this.options)
        } else {
          this.loading = false
        }
      })
    },

    // 通过初始值给options递归赋值
    async setInitOption(val, opts) {
      let v = val[0]
      const i = opts.findIndex(it => it[this.props.value] === v)
      if (i > -1) {
        let o = opts[i]
        this.selectedLabel.push(o[this.props.label])
        if (val.length > 1) {
          let res = await this.getValue(v)
          this.$set(o, this.props.children, this.getChildrenOptions(v, res))
          val.shift()
          return this.setInitOption(val, o.children)
        } else {
          this.loading = false
          return false
        }
      } else {
        this.loading = false
        return true
      }
    },

    // 不为倒数第二级、不超过设定的子级数，下一级加上children
    getChildrenOptions(value, list) {
      if (this.isLastLevel(value)) {
        for (let v of list) {
          const [hc, children] = this.hasChildren(v)
          if (hc) {
            v.children = children || []
          }
        }
      }
      return list
    },

    handleItemChange(node) {
      this.select = node.value
      this.$emit('change', this.select, node.pathLabels)

      if (!node.hasChildren) {
        this.toggleDropDownVisible(false)
      }
    },

    // 清空时还是要监听，并发送change事件
    handleCascaderChange(val) {
      this.select = val && val.length ? val[val.length - 1] : this.topValue
      this.$emit('change', this.select, [])
    },

    // 初始化选择：把类似'522300000000'初始化为['520000000000', '522300000000']
    getSelects(value) {
      if (!value) {
        return []
      }
      const level = this.getLevel(value)
      let count = 0
      const selects = []
      for (let i = 0; i <= level; i++) {
        count += this.structure[i]
        selects.push(value.substr(0, count) + Array(this.levelLength - count + 1).join('0'))
      }

      if (this.topValue) {
        selects.splice(0, selects.indexOf(this.topValue) + 1)
      }

      return selects
    },

    // 判断是否是最后一级(第3级设定的子级数)
    isLastLevel(value) {
      return this.getLevel(value) + this.lastDeepth >= this.lastLevel
    },

    // 获取级数
    getLevel(value) {
      if (!value) {
        return 0
      }
      const val = Number.parseInt(value, 10)
      if (isNaN(val)) {
        return 0
      }

      // 00 00 00 000 000
      // [2, 2, 2, 3, 3]
      let count = 0
      for (let i = this.structure.length - 1; i > 0; i--) {
        if ((val / Math.pow(10, count)) % Math.pow(10, this.structure[i]) > 0) {
          return i
        }
        count += this.structure[i]
      }
      return 0
    },

    // 通用获取数据方法
    getValue(id = '') {
      return this.fetchData(id, this.getLevel(id))
    },

    lazyLoad(node, resolve) {
      const value = node.value
      this.getValue(value)
        .then(res => {
          resolve(
            res.map(item => {
              const [hc] = this.hasChildren(item)
              const isLast = this.isLastLevel(value)
              item.leaf = !hc || isLast
              return item
            })
          )

          if (!this.hasInit) {
            this.hasInit = true
            if (this.autoInit) {
              this.init()
            }
          }
        })
        .catch(() => {
          resolve([])
        })
    },

    // element-ui 官方没有对外暴露的接口，版本更换时需小心
    toggleDropDownVisible(visible) {
      const ref = this.$children[0]
      ref && ref.toggleDropDownVisible(visible)
    },
  },
}
</script>
<style scoped></style>
